//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// OziExplorer WPT (waypoint) parser.
//

# include "StdAfx.h"
# include "Map.h"
# include "Types.h"
# include "Datum.h"
# include "Load.h"
# include "Log.h"
# include "Status.h"
# include "ParseHelpers.h"
# include "OziHelpers.h"
# include "fast_floor.h"
# include "Globals.h"

class COziWptLoader : public CMapLoader {
  public:
	// Override.
	virtual bool IsValid (const char * _pData, size_t _cFileSize) const;
	virtual LoadStatus_t Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap);

  private:
	void ParseWaypoint (const char * _strData, const char * _strEnd);
	const char * ParseLine (const char * _p);

	map_t * m_pMap;
	const char * m_pStart, * m_pEnd;

	// The counter of current line.
	size_t m_cFileLine;
	//
	size_t m_cOrdinal;

	const CTransformationBase * m_pTransformation;

	file_refs_t m_file_refs;
};

DECLARE_WPTRK_LOADER (COziWptLoader, "ozi-wpt", "wpt", "OziExplorer waypoints (*.wpt)", ftiWpts);

bool COziWptLoader::IsValid (const char * _pData, size_t _cFileSize) const {
	if (! CheckAnsiFile (_pData, _cFileSize, true))
		return false;

	return
		HasPrefix (_pData, _pData + _cFileSize, "OziExplorer Waypoint File",    25) ||
		HasPrefix (_pData, _pData + _cFileSize, "OziExplorer CE Waypoint File", 28);
}

void COziWptLoader::ParseWaypoint (const char * _strData, const char * _strEnd) {
	//
	// Parse fields.
	//

	const char * const strName         = OziFindNextField (_strData,        _strEnd);
	const char * const strLat          = OziFindNextField (strName,         _strEnd);
	const char * const strLon          = OziFindNextField (strLat,          _strEnd);
	const char * const strDateTime     = OziFindNextField (strLon,          _strEnd);
	const char * const strSymbol       = OziFindNextField (strDateTime,     _strEnd);
	const char * const strStatus       = OziFindNextField (strSymbol,       _strEnd);
	const char * const strDisplayFmt   = OziFindNextField (strStatus,       _strEnd);
	const char * const strFgColor      = OziFindNextField (strDisplayFmt,   _strEnd);
	const char * const strBgColor      = OziFindNextField (strFgColor,      _strEnd);
	const char * const strDescription  = OziFindNextField (strBgColor,      _strEnd);
	const char * const strPtrDir       = OziFindNextField (strDescription,  _strEnd);
	const char * const strGrmDispayFmt = OziFindNextField (strPtrDir,       _strEnd);
	const char * const strProximity    = OziFindNextField (strGrmDispayFmt, _strEnd);
	const char * const strAltitude     = OziFindNextField (strProximity,    _strEnd);

	// Name.
	string_t strWptName;
	OziParseString (strName, strLat, strWptName);

	// Description.
	string_t strWptDescription;
	if (strPtrDir != _strEnd)
		OziParseString (strDescription, strPtrDir, strWptDescription);
	else
		strWptDescription.clear ();

	// Position.
	const char * pEnd = NULL;
	point_t point;
	if (
		! ParseReal (strLat, _strEnd, & pEnd, point.y) ||
		! ParseReal (strLon, _strEnd, & pEnd, point.x)
	) {
		ReportWarning (_strData - m_pStart, "Unexpected format of waypoint data.");
		return;
	}

	//
	// Add the waypoint to the map.
	//

	rgn_t & rgn = m_pMap->CreateRgn (rkPoint);
	rgn.SetWaypointType ();

	rgn.pFileRef = & m_file_refs.back ();
	rgn.cBeginPos = _strData - m_pStart;
	rgn.cEndPos = _strEnd - m_pStart;
	rgn.cFileLine = m_cFileLine;
//	rgn.cOrdinal = m_cOrdinal ++;

	rgn.strLabel.swap (strWptName);
	rgn.strComment.swap (strWptDescription);

	rgn.origins.push_back (rgn_origin_t ());
	rgn_origin_t & origin = rgn.origins.back ();
	origin.cLevel = 0;
	origin.cFileOffset = _strData - m_pStart;
	origin.point = point;

	// Extra properties.
	waypoint_info_t * const pWptExtra = rgn.pExtra->wtr.CreateWaypoint ();
	
	// Date and Time.
	double dfDateTime = 0;
	if (ParseReal (strDateTime, _strEnd, & pEnd, dfDateTime)) {
		if (dfDateTime != 0) {
			pWptExtra->wFlags |= waypoint_info_t::defTime;
			pWptExtra->dtTime = dfDateTime;
		}
	}

	// Text color.
	unsigned long ulTextColor = 0;
	if (ParseDecU (strFgColor, strBgColor, & pEnd, ulTextColor)) {
		pWptExtra->wFlags |= waypoint_info_t::defTextColor;
		pWptExtra->colText = ulTextColor;
	}

	// Back color.
	unsigned long ulBackColor = 0;
	if (ParseDecU (strBgColor, strDescription, & pEnd, ulBackColor)) {
		pWptExtra->wFlags |= waypoint_info_t::defBackColor;
		pWptExtra->colBack = ulBackColor;
	}

	// Proximity.
	double fProximity = 0;
	if (ParseReal (strProximity, _strEnd, & pEnd, fProximity)) {
		if (fProximity != 0) {
			pWptExtra->wFlags |= waypoint_info_t::defProximity;
			pWptExtra->fProximity = fProximity;
		}
	}

	// Altitude.
	double fAltitudeInFeet = 0;
	if (ParseReal (strAltitude, _strEnd, & pEnd, fAltitudeInFeet)) {
		if (fAltitudeInFeet > -777) {
			pWptExtra->wFlags |= waypoint_info_t::defAltitude;
			pWptExtra->fAltitude = fAltitudeInFeet/c_feet_per_m;
		}
	}

//	pWptExtra->wGarminType = 12;
}

const char * COziWptLoader::ParseLine (const char * _p) {
	const char * const pNextLine = FindNextLine (_p, m_pEnd);

	if (_p [0] != '\n' && _p [0] != '\r') {
		if (m_cFileLine == 2) {
			// Get datum.
			const char * strDatumEnd = _p;
			while (strDatumEnd < pNextLine) {
				if (* strDatumEnd == ',' || * strDatumEnd == '\r' || * strDatumEnd == '\n')
					break;
				++ strDatumEnd;
			}
			const string_t strDatum (_p, strDatumEnd);

			if (! FindOziDatumToWGS84 (strDatum.c_str (), m_pTransformation)) {
				m_pTransformation = NULL;
				ReportWarning ("Some unsupported datum is used: '%s'. No datum conversion performed.", strDatum.c_str ());
			}
		} else if (m_cFileLine >= 5) {
			ParseWaypoint (_p, pNextLine);

			if (m_cFileLine == 5)
				m_pMap->cHeaderLen = _p - m_pStart;
		}
	}

	// Count lines.
	++ m_cFileLine;

	return pNextLine;
}

LoadStatus_t COziWptLoader::Load (const char * _pData, size_t _cFileSize, const char * _strFileName, map_t * _pMap) {
	m_pMap = _pMap;
	m_pStart = _pData;
	m_pEnd = _pData + _cFileSize;

	//
	// Parse .wpt file.
	//

	m_cFileLine = 1;
	m_cOrdinal = 0;

	m_pTransformation = NULL;

	m_file_refs.clear ();
	m_file_refs.push_back (file_ref_t ());
	m_file_refs.back ().strFileName = _strFileName;
	m_file_refs.back ().bAttachment = true;

	SetProgressStatus (0);

	const char * pLine = _pData;
	do {
		if (g_bStop)
			return lsInterrupted;
		pLine = ParseLine (pLine);
		SetProgressStatus (fast_floor ((50.f*(pLine - _pData))/_cFileSize + .5f));
	} while (pLine < _pData + _cFileSize);

	SetProgressStatus (50);

	// Convert datum.
	if (m_pTransformation) {
		ReportText ("Performing datum conversion to WGS84...");
		m_pMap->TransformPoints (* m_pTransformation);
	}

	m_pMap->file_refs.swap (m_file_refs);

	// Suppress warning about empty Zoom etc.
	m_pMap->bNoHeader = true;

	return lsOK;
}